﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;

namespace Nana.Web.Compressing
{
  public class CompressionModule
    : IHttpModule
  {
    internal static Type factoryInstnaceAttributeType;
    internal static Type factoryType;
    internal static readonly bool alwaysDoTextSub;
    internal static readonly CompressingEventChoice eventChoice;
    internal static List<CompressionTesterAction> typeTesterList;
    internal static List<CompressionFilterAction> dynamicFilterList;
    internal static List<CompressionFilterAction> staticFilterList;


    static CompressionModule()
    {
      factoryInstnaceAttributeType = typeof(FactoryInstanceAttribute);
      factoryType = typeof(ResponseFilterFactory);

      var config = ConfigurationManager.GetSection("nana.web/compressionModule") as CompressionModuleConfig;
      alwaysDoTextSub = config.ContentTypeTesters.AlwaysIncludeTextCategory;
      eventChoice = config.EventChoice;

      typeTesterList = config.ContentTypeTesters
        .Cast<CompressionTesterElement>()
        .Select(element => new CompressionTesterAction()
        {
          Name = element.Name,
          Pattern = new Regex(element.Pattern, element.Flags),
          ShouldCompress = element.ShouldCompress,
        })
        .ToList();

      dynamicFilterList = config.DynamicContentFilters
        .Cast<CompressionFilterElement>()
        .Select(element => new CompressionFilterAction()
        {
          Name = element.Name,
          Pattern = new Regex(element.Pattern, element.Flags),
          Factory = GetInstance(element.Type),
        })
        .ToList();

      staticFilterList = config.StaticContentFilters
        .Cast<CompressionFilterElement>()
        .Select(element => new CompressionFilterAction()
        {
          Name = element.Name,
          Pattern = new Regex(element.Pattern, element.Flags),
          Factory = GetInstance(element.Type),
        })
        .ToList();
    }

    private static ResponseFilterFactory GetInstance(Type type)
    {
      //if (!factoryType.IsAssignableFrom(type))
      //  throw new ArgumentOutOfRangeException();

      foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Static))
      {
        if (prop.CanRead && factoryType.IsAssignableFrom(prop.PropertyType) &&
          prop.GetCustomAttributes(factoryInstnaceAttributeType, false).Length == 1)
          return (ResponseFilterFactory)prop.GetValue(null, null);
      }

      var ctor = type.GetConstructor(Type.EmptyTypes);
      return (ResponseFilterFactory)ctor.Invoke(null);
    }

    public void Dispose()
    {
    }


    public void Init(HttpApplication context)
    {
      // 这个方法代理必须含有对 HttpApplication context 的引用，以保证行为正确
      EventHandler handler = (sender, e) =>
      {
        // 主要操作都在 HttpContext 上，为了简化代码而将其提取为静态方法
        SetupCompressFilter(context.Context);
      };

      switch (eventChoice)
      {
        case CompressingEventChoice.PostResolveRequestCache:
          context.PostResolveRequestCache += handler;
          break;
        case CompressingEventChoice.PostMapRequestHandler:
          context.PostMapRequestHandler += handler;
          break;
        case CompressingEventChoice.AcquireRequestState:
          context.AcquireRequestState += handler;
          break;
        case CompressingEventChoice.PostAcquireRequestState:
          context.PostAcquireRequestState += handler;
          break;
        case CompressingEventChoice.PreRequestHandlerExecute:
          context.PreRequestHandlerExecute += handler;
          break;

        case CompressingEventChoice.PostRequestHandlerExecute:
          context.PostRequestHandlerExecute += handler;
          break;
        case CompressingEventChoice.ReleaseRequestState:
          context.ReleaseRequestState += handler;
          break;
        case CompressingEventChoice.PostReleaseRequestState:
          context.PostReleaseRequestState += handler;
          break;

        // don't use this
        //case CompressingEventChoice.BeginRequest:
        //  context.BeginRequest += handler;
        //  break;

        default:
          throw new InvalidOperationException();
      }

      //app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
      //steps.Add(new HttpApplication.MapHandlerExecutionStep(app));
      //app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
      //app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
      //app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
      //app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
    }

    private static void SetupCompressFilter(HttpContext context)
    {
      var response = context.Response;
      // 只需要提供 HttpContext 和 HttpResponse.Filter，其它事情让专业的来
      response.Filter = new ResponseCompressFilter(context, response.Filter);
    }
  }
}
